import asyncio
import time
import re
import json
import urllib.parse
from typing import Dict, Any, List, Optional
import httpx
from config.setting import FEISHU_APP_ID, FEISHU_APP_SECRET

import logging

from app.utils.logger import get_logger

# 传入当前模块名，获取已配置好的日志器
logger = get_logger(__name__)



def clean_json_string(json_str: str) -> str:
    """清理JSON中的无效字符（提升为模块级函数，供外部调用）"""
    import re
    # 过滤不可见控制字符（解决Invalid control character错误）
    cleaned = re.sub(r'[\x00-\x1F\x7F\u200d]', '', json_str)
    # 保留有效空白符（换行、制表符）
    cleaned = ''.join([c for c in cleaned if ord(c) >= 32 or c in '\t\n\r'])
    return cleaned


class FeishuSheetManager:
    """飞书表格管理器（添加读取功能）"""

    def __init__(self):
        self.app_id = FEISHU_APP_ID
        self.app_secret = FEISHU_APP_SECRET
        self.tenant_access_token = None
        self.token_expire_time = 0
        logger.info("FeishuSheetManager 初始化完成")

    async def close(self):
        """关闭 httpx 异步客户端，释放资源"""
        if hasattr(self, 'async_client'):
            await self.async_client.aclose()
            logger.info("FeishuSheetManager 异步客户端已关闭")

    async def get_tenant_access_token(self) -> str:
        """获取飞书API令牌（带缓存）"""
        current_time = time.time()
        if self.tenant_access_token and self.token_expire_time > current_time + 60:
            return self.tenant_access_token

        try:
            async with httpx.AsyncClient() as client:
                response = await client.post(
                    "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
                    json={"app_id": self.app_id, "app_secret": self.app_secret},
                    timeout=10
                )
                response.raise_for_status()
                result = response.json()
                if result.get("code") != 0:
                    raise Exception(f"获取令牌失败: {result.get('msg')}")

                self.tenant_access_token = result["tenant_access_token"]
                self.token_expire_time = current_time + result.get("expire_in", 3600)
                return self.tenant_access_token
        except Exception as e:
            logger.error(f"令牌获取失败: {str(e)}")
            raise

    async def get_sheet_id(self, spreadsheet_token: str) -> str:
        """获取表格中第一个工作表的sheet_id"""
        token = await self.get_tenant_access_token()
        try:
            async with httpx.AsyncClient() as client:
                response = await client.get(
                    f"https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{spreadsheet_token}/metainfo",
                    headers={"Authorization": f"Bearer {token}"},
                    timeout=10
                )
                response.raise_for_status()
                result = response.json()
                if result.get("code") != 0:
                    raise Exception(f"获取工作表失败: {result.get('msg')}")

                sheets = result["data"].get("sheets", [])
                if not sheets:
                    raise Exception("表格中无工作表")

                return sheets[0].get("sheetId") or sheets[0].get("sheet_id") or "0"
        except Exception as e:
            logger.error(f"获取sheet_id失败: {str(e)}")
            raise

    async def write_to_cells(self, spreadsheet_token: str, sheet_id: str, cell_data: Dict[str, str]) -> Dict[str, Any]:
        """写入数据到指定单元格（支持B9、B10、A29-F29等）"""
        token = await self.get_tenant_access_token()
        try:
            value_ranges = [{
                "range": f"{sheet_id}!{cell}:{cell}",
                "values": [[value]]
            } for cell, value in cell_data.items()]

            async with httpx.AsyncClient() as client:
                response = await client.post(
                    f"https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{spreadsheet_token}/values_batch_update",
                    headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
                    json={"valueRanges": value_ranges},
                    timeout=15
                )
                response.raise_for_status()
                result = response.json()

            if result.get("code") == 0:
                return {"status": "success", "message": "写入成功"}
            else:
                return {"status": "error", "message": f"写入失败: {result.get('msg')}"}
        except Exception as e:
            logger.error(f"单元格写入失败: {str(e)}")
            return {"status": "error", "message": str(e)}

    async def read_range(self, spreadsheet_token: str, range: str, value_render_option: str = "ToString") -> Optional[
        List[List[Any]]]:
        """读取电子表格中指定范围的数据（根据官方示例代码实现）"""
        token = await self.get_tenant_access_token()
        try:
            # 编码range参数
            encoded_range = urllib.parse.quote(range)

            # 构建查询参数
            params = {
                "valueRenderOption": value_render_option,
                "dateTimeRenderOption": "FormattedString"
            }

            async with httpx.AsyncClient() as client:
                response = await client.get(
                    f"https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{spreadsheet_token}/values/{encoded_range}",
                    headers={"Authorization": f"Bearer {token}"},
                    params=params,
                    timeout=10
                )
                response.raise_for_status()
                result = response.json()

                if result.get("code") != 0:
                    raise Exception(f"读取范围失败: {result.get('msg')}")

                # 返回读取到的值
                value_range = result.get("data", {}).get("valueRange", {})
                values = value_range.get("values", [])
                logger.info(f"成功读取范围 {range}，获取到 {len(values)} 行数据")
                return values

        except Exception as e:
            logger.error(f"读取范围失败: {str(e)}")
            return None


class FeishuSpreadsheetUtil:
    """飞书表格工具（添加读取功能）"""

    def __init__(self):
        self.sheet_manager = FeishuSheetManager()

    async def write_to_existing_sheet(self, spreadsheet_token: str, video_script: dict, strategy_result: str,
                                      shot_list: list = None) -> Dict[str, Any]:
        """写入数据到已有表格的指定单元格（B9、B10、A29-F29）"""
        try:
            # 1. 获取工作表ID
            sheet_id = await self.sheet_manager.get_sheet_id(spreadsheet_token)
            logger.info(f"使用已有表格: {spreadsheet_token}, sheet_id: {sheet_id}")

            # 2. 解析视频脚本（提取文本和标签）- 直接传字典
            text, label = self._parse_video_script(video_script)

            # 3. 准备单元格数据
            cell_data = {
                "B9": text[:1000],  # 限制长度避免溢出
                "B10": label[:100]
            }

            # 4. 处理分镜列表
            if shot_list:
                for i, shot in enumerate(shot_list):
                    row = 29 + i
                    if isinstance(shot, dict):
                        cell_data[f"A{row}"] = shot.get("景别", "")
                        cell_data[f"B{row}"] = shot.get("画面", "")
                        cell_data[f"C{row}"] = shot.get("口播", "")
                        cell_data[f"D{row}"] = shot.get("花字", "")
                        cell_data[f"E{row}"] = shot.get("时长", "")
                        cell_data[f"F{row}"] = shot.get("备注", "")
                    else:
                        cell_data[f"A{row}"] = str(shot)[:50]

            # 5. 执行写入
            result = await self.sheet_manager.write_to_cells(
                spreadsheet_token=spreadsheet_token,
                sheet_id=sheet_id,
                cell_data=cell_data
            )
            return result

        except Exception as e:
            return {"status": "error", "message": f"处理失败: {str(e)}"}

    async def read_range(self, spreadsheet_token: str, range: str, value_render_option: str = "ToString") -> Optional[
        List[List[Any]]]:
        """读取电子表格中指定范围的数据"""
        return await self.sheet_manager.read_range(spreadsheet_token, range, value_render_option)

    def _parse_video_script(self, video_script: dict) -> tuple:
        """解析视频脚本，提取文本和标签（适配字典输入）"""
        try:
            # 直接处理字典，无需先解析JSON字符串
            if "raw_content" in video_script:
                raw_content = clean_json_string(video_script["raw_content"])  # 使用模块级函数
                raw_data = json.loads(raw_content)
                return raw_data.get("text", ""), raw_data.get("label", "")
            # 若没有 raw_content，直接取 text 和 label 字段
            return video_script.get("text", ""), video_script.get("label", "")
        except Exception as e:
            logger.error(f"解析视频脚本失败: {str(e)}, 脚本预览: {str(video_script)[:150]}")
            return "", "自动生成（解析失败）"